//
//  STAppOpenAdmobObject.swift
//  STAppOpenAdmobObject
//
//  Created by Toj on 2/15/23.
//  Copyright © 2023 Google. All rights reserved.
//

import UIKit
import GoogleMobileAds

/* 开屏问题:
 1. 登录结束完成后广告回来了, 还展示吗?
 2. 页面按 Home退到后台, 再回来展示吗?
 3. 在展示开屏广告的时候 按Home, 回来是关闭的, admob Demo即如此, 尝试无法不关闭
 */
class STAppOpenAdmobObject: STAdmobObject {
    
    /* 广告对象 */
    typealias AdObject = GADAppOpenAd
    
    /* 广告展示开始时间: 用来报数展示多长时间 */
    private var startTime: CFAbsoluteTime = 0
    
    /* 广告状态 */
    private var loadState: STAdmobAdState = .normal
    
    // 广告预加载时间, 可能会超时
    private var preloadedTime: CFTimeInterval = 0
    /* 预加载广告数据: 用来预加载 广告平台对象 */
    private var _preloadAdmobModel: STAdmobModel?

    /* 预加载广告对象: 借助广告数据加载后的 广告平台对象 */
    private var _preloadedGAd: AdObject? { didSet {
        guard _preloadedGAd != nil else {
            loadState.remove([.preloading,.preloaded])
            return
        }
        loadState.remove(.preloading)
        loadState.insert(.preloaded)
    } }
    
    // 广告加载时间, 可能会超时
    private var loadedTime: CFTimeInterval = 0
    /* 加载广告数据: 用来加载 广告平台对象 */
    private var _admobModel: STAdmobModel?
    /* 加载广告对象: 借助广告数据加载后的 广告平台对象 */
    private var _loadedGAd: AdObject? { didSet {
        guard let _ = _loadedGAd else {
            loadState.remove([.loading,.loaded, .showing, .showed])
            return
        }
        _loadedGAd?.fullScreenContentDelegate = self
        loadState.remove(.loading)
        loadState.insert(.loaded)
    } }
    
    // 判断广告是否有效
    private var isAdAvailable: Bool {
        guard loadedTime > 0 else { return false }
        
        let currentTime = CFAbsoluteTimeGetCurrent()
        let timeInterval = currentTime - loadedTime
        let isLessThan = timeInterval < timeoutInterval
        let isAvailable = _loadedGAd != nil && loadState.isLoaded && isLessThan
        // 广告有效 = 广告存在 && 广告加载完成 && 广告未过期
        return isAvailable
    }
    
    /* 广告有效时间: 4小时有效时间 */
    private let timeoutInterval: TimeInterval = 4 * 3_600
}

// MARK: - 广告协议
extension STAppOpenAdmobObject: STAdmobProtocol {
    
    var delegate: STAdmobObjectDelegate? {
        get { return _delegate }
        set { _delegate = newValue }
    }
    
    /*
     * 读取是否有 预加载完成的 广告对象
     */
    func isPreparedAd(admobModel: STAdmobModel) -> Bool {
        var isPrepared = _preloadedGAd != nil
        if isPrepared {
            responseLoadAd(admobModel: admobModel, adObject: _preloadedGAd, error: nil)
            loadedTime = preloadedTime
            _preloadedGAd = nil
            
            // 广告对象是否有冲突
            isPrepared = isAdAvailable
        }
        return isPrepared
    }
    
    /* 预加载 广告对象 */
    func preloadCacheAd(admobModel: STAdmobModel) {
        print("-->toj: admob开屏 准备预加载广告, UnitID:\(admobModel.materials)")
        if loadState.isPreload { return }
        loadState.insert(.preloading)
        
        print("-->toj: admob开屏 预加载广告, UnitID:\(admobModel.materials)")
        requestLoadAd(admobModel: admobModel) { [weak self] ad, error in
            self?.responsePreloadAd(admobModel: admobModel, adObject: ad, error: error)
            print("-->toj: admob开屏 预加载广告 完成:, UnitID:\(admobModel.materials)")
        }
    }
    
    /* 更新广告 */
    func replacePreloadCacheAd(admobModel: STAdmobModel, completion: ((Bool) -> Void)?) {
        print("-->toj: admob开屏 更新预加载广告, UnitID:\(admobModel.materials)")
        _preloadedGAd = nil
        
        loadState.insert(.preloading)
        print("-->toj: admob开屏 更新后预加载广告, UnitID:\(admobModel.materials)")
        requestLoadAd(admobModel: admobModel) { [weak self] ad, error in
            self?.responsePreloadAd(admobModel: admobModel, adObject: ad, error: error)
            let isSuccess = ad != nil
            completion?(isSuccess)
        }
    }
    
    /*
     * 展示 预加载 广告对象,
     * 有预加载 广告对象: 待展示
     * 无预加载 广告对象: 加载 广告对象, 加载完后缓存广告对象 待展示
     */
    func preloadAd(admobModel: STAdmobModel, completion: ((Bool) -> Void)?) {
        print("-->toj: admob开屏 准备预加载广告, UnitID:\(admobModel.materials)")
        guard _preloadedGAd != nil else {
            print("-->toj: admob开屏 无 预缓存对象, 开始缓存广告对象, UnitID:\(admobModel.materials)")
            loadAd(admobModel: admobModel, completion: completion)
            return
        }
        
        print("-->toj: admob开屏 有 预缓存对象, UnitID:\(admobModel.materials)")
        if loadState.isLoading {
            completion?(false)
            return
        }
        loadState.insert(.loading)
        print("-->toj: admob开屏 有 预缓存对象, 待展示, UnitID:\(admobModel.materials)")
        
        // 更新最新广告对象
        // 保存广告数据
        responseLoadAd(admobModel: admobModel, adObject: _preloadedGAd, error: nil)
        loadedTime = preloadedTime
        // 清空预缓存对象
        preloadedTime = 0
        _preloadedGAd = nil
        
        if isAdAvailable {
            completion?(true)
            return
        }
        print("-->toj: admob开屏 超时, UnitID:\(admobModel.materials)")
        loadAd(admobModel: admobModel, completion: completion)
    }
    
    /* 加载新广告 */
    func loadAd(admobModel: STAdmobModel, completion: ((Bool) -> Void)?) {
        print("-->toj: admob开屏 准备加载全新广告, UnitID:\(admobModel.materials)")
        if loadState.isLoading {
            completion?(false)
            return
        }
        loadState.insert(.loading)
        print("-->toj: admob开屏 开始加载最新广告, UnitID:\(admobModel.materials)")
        requestLoadAd(admobModel: admobModel) { [weak self] ad, error in
            self?.responseLoadAd(admobModel: admobModel, adObject: ad, error: error)
            let isSuccess = ad != nil
            completion?(isSuccess)
        }
    }
    
    /* 展示广告 */
    func presentAd(from vc: UIViewController) {
        print("-->toj: admob开屏 广告对象即将显示.")
        
        if !loadState.isLoaded {
            if loadState.isShowed {
                print("-->toj: admob开屏 广告对象已经展示.")
                return
            }
            print("-->toj: admob开屏 广告对象正在加载.")
            return
        }
        
        print("-->toj: admob开屏 展示")
        
        _admobModel?.isShowed = false
        // 有待展示广告
        _loadedGAd?.present(fromRootViewController: vc)
    }
}

private extension STAppOpenAdmobObject {
    
    /* 请求广告, 有超时时间, 所以不需要做缓存判断 */
    func requestLoadAd(admobModel: STAdmobModel,
                       completion: @escaping ((_ ad: AdObject?, _ error: Error?)->Void)) {
        print("-->toj: admob开屏 加载广告对象, UnitID:\(admobModel.materials)")
        
        AdObject.load(withAdUnitID: admobModel.materials,
                          request: GADRequest(),
                          orientation: .portrait) { ad, error in
            print("-->toj: admob开屏 新广告对象返回")
            completion(ad, error)
        }
    }
    
    /* 预加载广告回调 */
    func responsePreloadAd(admobModel: STAdmobModel,
                           adObject: AdObject?,
                           error: Error?) {
        print("-->toj: admob开屏 预加载广告 响应,  UnitID:\(admobModel.materials)")
        guard _preloadedGAd == nil else { return }
        _preloadedGAd = adObject
        if let error = error {
            print("-->toj: admob开屏 预加载广告 响应 失败: \(error.localizedDescription), UnitID: \(admobModel.materials)")
            return
        }
        print("-->toj: admob开屏 预加载广告 响应 成功, UnitID:\(admobModel.materials)")
        preloadedTime = CFAbsoluteTimeGetCurrent()
        _preloadAdmobModel = admobModel
    }
    
    /* 实时加载广告回调 */
    func responseLoadAd(admobModel: STAdmobModel,
                        adObject: AdObject?,
                        error: Error?)  {
        _loadedGAd = adObject
        
        if let error = error {
            print("-->toj: admob开屏 加载失败: \(error.localizedDescription), UnitID:\(admobModel.materials)")
            return
        }
        
        loadedTime = CFAbsoluteTimeGetCurrent()
        _admobModel = admobModel
        print("-->toj: admob开屏 加载成, UnitID:\(admobModel.materials)")
    }
    
    /* 清除广告 */
    func clearAd() {
        print("-->toj: admob开屏 清除广告")
        _loadedGAd = nil
        loadedTime = 0
    }
}

// MARK: - 广告 GADFullScreenContentDelegate 回调
extension STAppOpenAdmobObject: GADFullScreenContentDelegate {
    
    // 广告展示
    func adWillPresentFullScreenContent(_ ad: GADFullScreenPresentingAd) {
        print("-->toj: admob开屏 广告展示")
        
        startTime = CFAbsoluteTimeGetCurrent()
        
        loadState.insert(.showing)
        delegate?.admobObjectDidPresentContent(self, admobModel: _admobModel)
    }
    
    // 广告展示失败
    func ad(_ ad: GADFullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
        
        print("-->toj: admob开屏 广告展示失败")
        
        delegate?.admobObject(self, admobModel: _admobModel, didFailToPresentFullScreenContent: error)
        clearAd()
    }
    
    // 广告即将退出
    func adWillDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
        print("-->toj: admob开屏 广告即将退出")
        let endTime = CFAbsoluteTimeGetCurrent()
        let showedTime = endTime - startTime
        print("-->toj: admob开屏 time:\(showedTime)")
        delegate?.admobObjectWillDismissComplete(self, admobModel: _admobModel, showedTime: showedTime)
    }
    
    // 广告退出
    func adDidDismissFullScreenContent(_ ad: GADFullScreenPresentingAd) {
        print("-->toj: admob开屏 广告退出")
        delegate?.admobObjectDidDismissComplete(self, admobModel: _admobModel)
        clearAd()
    }
    
    // 广告点击
    func adDidRecordClick(_ ad: GADFullScreenPresentingAd) {
        print("-->toj: admob开屏 广告点击")
        delegate?.admobObjectDidRecordClick(self, admobModel: _admobModel)
    }
}
